#include "demo3.h"
#include "led.h"

// // void test1(void);

// // 外部中断，也是AFIO（端口的复用功能）

// static void gpio_init(void)
// {
//     // PA.0
//     __HAL_RCC_GPIOA_CLK_ENABLE(); // 开启时钟
//     __HAL_RCC_AFIO_CLK_ENABLE();  // 开启复用功能时钟

//     GPIO_InitTypeDef initTypeDef = {
//         .Pin  = GPIO_PIN_0,
//         .Mode = GPIO_MODE_IT_RISING, // 检测中断上升沿信号(一般情况下，只有外部中断，选择对应的，否则的，只需要选择普通的输入就可以了)
//         .Pull = GPIO_PULLDOWN,       // 默认下拉
//     };                               // 推荐使用驼峰命名

//     HAL_GPIO_Init(GPIOA, &initTypeDef);

//     // test1();

//     // 要配置NVIC
//     HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 配置中断优先级，0是最大，15是最小的
//     // 一般情况下，可以将优先级分组，设置位第4组，主优先级占4bit,子优先级占0bit
//     HAL_NVIC_EnableIRQ(EXTI0_IRQn);

//     // 开启外部中断就可以了
//     // 直接使用GPIO的EXTI，可以不用单独设置
// }

// void demo3_test(void)
// {
//     led_init(); // 初始化LED

//     // 中断，PA0 作为EXTI的外部输入中断
//     gpio_init();

//     while (1) {
//     }
// }

// // 先配置，将来外部中断触发之后，需要运行到哪个函数上？
// // 所有的中断处理函数，都是无返回值，无参数的
// void EXTI0_IRQHandler(void)
// {
//     HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // HAL希望我们，在中断处理，直接回调HAL的中断处理函数
// }

// /// @brief 直接重写回调
// /// @param GPIO_Pin
// void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
// {
//     if (GPIO_Pin == GPIO_PIN_0) {
//         LED = !LED; // 翻转LED,可能会存在LED灯状态不稳定的问题，是因为物理按键，有抖动。
//     }
// }

/////寄存器开发

static void gpio_exti_init(void)
{
    // 开启时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_AFIO_CLK_ENABLE();

    // 寄存器的开发，就像是做“完形填空(翻译题)”，而库函数，更像是“阅读理解”，API文档

    // 需要把GPIO设置为复用功能（复用功能，必须要先对GPIO进行初始化）
    // 0.1.配置GPIO为输入模式
    GPIOA->CRL &= ~0x03;
    // 0.2.配置为上下拉输入模式
    GPIOA->CRL &= ~(0x03 << 2);
    GPIOA->CRL |= (0x02 << 2); // 配置为上下拉模式
    // 0.3.设置为默认下拉状态
    GPIOA->ODR &= ~(1 << 0); // 下拉
    // 0.4.配置EXTI中断线
    AFIO->EXTICR[0] &= ~0x0F;

    // 1.1.配置上升沿或者下降沿的选择模式
    EXTI->RTSR |= 1 << 0; // TR0设置位1，表明，EXTI0，选择上升沿触发
    // 1.2.可以通过读或者写挂起寄存器，获得或者操作相关的状态
    // 1.3.需要通过配置中断屏蔽寄存器，选择是否开启EXTI0
    EXTI->IMR |= 1 << 0; // 开启EXTI0的中断

    // 当中断触发的时候，会送达到NVIC
    // 2.1.配置优先级分组
    NVIC_SetPriority(EXTI0_IRQn, 0); // 第二个参数是优先级等级
    NVIC_EnableIRQ(EXTI0_IRQn);      // 开启中断
}

void demo3_test(void)
{
    led_init();
    gpio_exti_init();
    while (1) {
    }
}

// 定义EXTI0的中断处理函数
void EXTI0_IRQHandler(void)
{
    // 1.2.可以通过读或者写挂起寄存器，获得或者操作相关的状态
    if ((EXTI->PR & 0x01) != 0x00) {
        // 可以做操相关的处理了
        EXTI->PR |= 0x01; // 需要清除该位(写1的方式进行清除)

        LED = !LED; // 处理业务逻辑，做好放在下面
    }
}